﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Tuxedo.Compile
{
	internal class UnverifiedType
	{
		public string Name { get; private set; }
		public int ArrayDimensionality { get; private set; }
		public Expression ArraySize { get; private set; }
		public UnverifiedType[] Templates { get; private set; }
		public bool IsArray { get { return this.ArrayDimensionality > 0; } }

		public UnverifiedType(string name, IList<UnverifiedType> templates, int arrayDimensionality, Expression arraySize)
		{
			this.Name = name;
			this.Templates = templates.ToArray();
			this.ArrayDimensionality = arrayDimensionality;
			this.ArraySize = arraySize;
		}

		public static UnverifiedType Parse(TokenStream tokens, bool failSilently)
		{
			return Parse(tokens, failSilently, false);
		}

		public static UnverifiedType Parse(TokenStream tokens, bool failSilently, bool allowArraySizeDeclaration)
		{
			string name = ParseDottedExpression(tokens);
			if (name == null)
			{
				if (failSilently)
				{
					return null;
				}
				else
				{
					throw new Exception("Type expected");
				}
			}

			List<UnverifiedType> templates = new List<UnverifiedType>();
			if (tokens.PopIfPresent("<"))
			{
				UnverifiedType templateType = UnverifiedType.Parse(tokens, failSilently);
				templates.Add(templateType);
				while (tokens.PopIfPresent(","))
				{
					templates.Add(UnverifiedType.Parse(tokens, failSilently));
				}

				tokens.PopExpected(">");
			}

			Expression arraySize = null;
			int arrayDimensionality = 0;
			bool first = true;
			while (tokens.PopIfPresent("["))
			{
				arrayDimensionality++;
				if (!tokens.IsNext("]") && first)
				{
					arraySize = Expression.ParseExpression(tokens);
				}
				first = false;
				if (!tokens.PopIfPresent("]"))
				{
					if (failSilently) return null;
					tokens.PopExpected("]");
				}
			}

			return new UnverifiedType(name, templates, arrayDimensionality, arraySize);
		}

		public static string ParseDottedExpression(TokenStream tokens)
		{
			//tokens.SaveState();

			string value = tokens.PeekValue();
			if (!IsValidIdentifier(value))
			{
				return null;
			}
			tokens.Pop();

			while (tokens.PopIfPresent("."))
			{
				string identifier = tokens.PopIdentifier();

				value += "." + identifier;
			}

			return value;
		}

		private static readonly HashSet<char> VALID_CHARS = new HashSet<char>(
			"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789".ToCharArray());

		private static bool IsValidIdentifier(string stuff)
		{
			if (stuff[0] >= '0' && stuff[0] <= '9')
				return false;

			for (int i = stuff.Length - 1; i >= 0; --i)
			{
				if (!VALID_CHARS.Contains(stuff[i]))
				{
					return false;
				}
			}
			return true;
		}
	}
}
